#ifndef __CAPAINDICES_ARBOLBMAS_ESTRATEGIABORRADO_CPP_
#define __CAPAINDICES_ARBOLBMAS_ESTRATEGIABORRADO_CPP_

/*****************************************************************************
*                     Organizaci�n de Datos - 75.06                          *
*                         C�tedra Lic. Servetto                              *
*            Facultad de Ingenier�a - Universidad de Buenos Aires            *
******************************************************************************/ 

#include "ArbolBMas.h"
#include <sstream>
#include <iostream>

using namespace std;
using namespace Comun;

namespace CapaIndices
{
	EstrategiaEliminacion::EstrategiaEliminacion(CapaIndices::ArbolBMas *arbol, bool multiple):Estrategia(arbol,multiple){
		this->Multiple = multiple;
	}

	EstrategiaEliminacion::~EstrategiaEliminacion(){
	}

	
	/*** Declaracion de Metodos Privados ***/
	// Elimina los registros en el indice secundario y ejecuta la eliminacion tambien sobre el primario


	/*** Implementacion de metodos abstractos de Estrategia ***/

	void ActualizarCantidad(Grupo* grupo){

		for (IteradorDeAtributos i = grupo->Atributos.begin();i!=grupo->Atributos.end();i++)
		{
			if ((*i).Nombre == "Cantidad")
			{
				stringstream* ss = new stringstream();
				*ss << grupo->Registros.size();

				(*i).Valor = ss->str();

				delete ss;
			}
		}
	}

	Resultado* EstrategiaEliminacion::BorradoRecursivo(Clave* clave, Nodo* nodo,Nodo** padre, Registro* localizador,bool hallado){

		Nodo** siguiente = new Nodo*;				
		*siguiente = NULL;
		bool haySiguiente = false;

		// Si se trata de una eliminacion multiple, verifica si las condiciones sobre la clave
		// requieren que se lea alguno de los nodos hermanos (menor o mayor).
		if (Multiple && (padre != NULL))
			haySiguiente = VerificarRangoCondicion(clave,nodo,padre,localizador,siguiente);

		if (Arbol->Metadata->Tipo == Griego)
		{
			if (Arbol->Metadata->TipoDato == Polivalente)
				BorrarRegistrosEnPolivalente(nodo,clave,localizador);
			else
				BorrarRegistrosEnMonovalente(nodo,clave,localizador);
		}
		else
		{
			if (hallado)
			{
				BorrarRegistrosEnSecundario(nodo,clave,localizador,*Arbol->Metadata);
			}
			else
			{
				for (IteradorDeRegistros n = (nodo)->Registros.begin();n!=(nodo)->Registros.end();n++)
				{
					if (clave->OtrasCondiciones->Analizar(*n))
					{
						Nodo* nod = new Nodo(Arbol->ArchivoDatos->LeerGrupo(obtenerIdHijoDerecho(*n)));
						BorrarRegistrosEnSecundario(nod,clave,localizador,*Arbol->Metadata);
					}
				}
			}
		}

		clave->FlushResultados(false);

		if (Multiple && haySiguiente)
		{
			Camino.clear();
			// Actualiza el camino para poder trabajar sobre el nodo siguiente de datos
			// Primero revisa si la fusion no incluyo valores al nodo que deban ser eliminados
			bool actualizar = false;

			for (IteradorDeRegistros i = nodo->Registros.begin(); i!= nodo->Registros.end() ; i++)
			{
				// Verifica que se cumplan otras condiciones				
				if (clave->OtrasCondiciones->Analizar(*i))
					actualizar = true;				
			}

			if (actualizar)
				return &EjecutarEstrategia(clave);

			// Vuelve a verificar los rangos de condición para verificar si el nodo siguiente no fue borrado
			// por una fusion.
			VerificarRangoCondicion(clave,nodo,padre,localizador,siguiente);

			Registro r = *(*siguiente)->Registros.begin();
			Clave* c = new Clave();
			list<Atributo>& meta = (*ExtraerAtributosClave(&(r.Atributos),Arbol->Metadata->Claves));

			for (IteradorDeAtributos a = meta.begin();a != meta.end(); a++)
				c->ValoresBusqueda.insert(c->ValoresBusqueda.end(),(*a).Valor);

			c->OtrasCondiciones = clave->OtrasCondiciones;
			c->RegistrosAfectados = clave->RegistrosAfectados;
			c->Resultado = clave->Resultado;
			c->EjecucionMultiple = clave->EjecucionMultiple;
			c->NombreArchivo = clave->NombreArchivo;
			c->primerFlush = clave->primerFlush;

			return &EjecutarEstrategia(c);
		}
		else
			return new Resultado();

	}

	Resultado EstrategiaEliminacion::EjecutarEstrategia(CapaIndices::Clave *clave){

		//	encontrar el nodo hoja que contiene el valor
		Resultado r = LocalizarClave(clave);

		Nodo* nodo = NULL;
		Nodo** padre = NULL;

		if (Camino.size() >= 2)
		{
			// Nodo donde se encuentra el localizador
			nodo = (*--Camino.end());
			padre = new Nodo*;
			*padre = (*--(--Camino.end()));
		}else
		{
			// Nodo donde se encuentra el localizador
			nodo = (*--Camino.end());
		}

		// Obtiene el localizador
		Registro* localizador = NULL;

		if (clave->Resultado->size()>0)
			localizador = &(* clave->Resultado->begin());

		bool hallado = ( r.Codigo == Exito);

		// Borra los registros en recursando a traves de los nodos y sus hermanos, si el borrado es multiple
		return *BorradoRecursivo(clave,nodo,padre,localizador,hallado);
	}

	/*** Implementacion de Metodos Privados ***/

	void EstrategiaEliminacion::BorrarRegistrosEnSecundario(Nodo* nodo, Clave* clave,Registro* localizador, MetadatosIndice metadata)
	{
		//Grupo g = Arbol->ArchivoDatos->LeerGrupo(obtenerIdHijoDerecho(*nodo->Registros.begin()));

		// Obteniene el indice principal a partir de la subcapa de seleccion de indices
		IIndice* indicePrimario = ObtenerIndicePrimario(Arbol->Metadata->NombreClase);		

		list<Registro>* registrosAEliminar = new list<Registro>();

		// Por cada clave del indice primario, intenta eliminar el registro en el mismo
		for (IteradorDeRegistros i = nodo->Registros.begin(); i != nodo->Registros.end(); i++ )
		{
			// Analizo si la clave primaria sigue cumpliendo la condicion
			if (clave->OtrasCondiciones->Analizar(*i))
			{
				registrosAEliminar->push_back(*i);				
			}
		}

		int size = registrosAEliminar->size();
		// Genera un array auxiliar para poder eliminar los elementos de la lista
		// Esto es necesario porque sino se debe utilizar la misma lista para iterar los elementos
		// y el iterador pierde la posicion actual en cada eliminacion
		Registro** auxiliar = new Registro*[size];
		IteradorDeRegistros iterador = registrosAEliminar->begin();

		for (int i =0; i < size ; i++)	
			auxiliar[i] = &(* iterador++);

		for (int i =0; i < size ; i++)	
		{
			clave->RegistrosAfectados++;		

			nodo->Registros.remove(*auxiliar[i]);	

			for (IteradorDeMetadatosAtributos atr = Arbol->Metadata->Claves.begin();atr != Arbol->Metadata->Claves.end(); atr++)
			{
				for (IteradorDeAtributos atr2 = nodo->Atributos.begin();atr2 != nodo->Atributos.end(); atr2++)
					if (atr2->Nombre == atr->Nombre)
					{
						auxiliar[i]->Atributos.push_back(*atr2);
					}
			}

			clave->Resultado->push_back(*auxiliar[i]);
		}

		ActualizarCantidad(nodo);
		Arbol->ArchivoDatos->GuardarGrupo(*nodo,true);

		// libera el array de punteros (no los datos a los que apuntaban los punteros).
		delete auxiliar;

	}

	void EstrategiaEliminacion::BorrarRegistrosEnMonovalente(Nodo* nodo, Clave* clave,Registro* localizador){
		
		if (clave->OtrasCondiciones->Analizar(*nodo->Registros.begin()))
		{
			
			// Agrega el registro eliminado al conjunto de resultados
						
			if (clave->Resultado->size() == 1)
			{
				Registro primero = *clave->Resultado->begin();
				if ( !( primero == (*nodo->Registros.begin()) ) )
				{
					clave->RegistrosAfectados++;										
					// Agrega el registro eliminado al conjunto de resultados
					clave->Resultado->insert(clave->Resultado->end(),(*nodo->Registros.begin()));
				}
			}
			else
			{
				clave->RegistrosAfectados++;							
				// Agrega el registro eliminado al conjunto de resultados
				clave->Resultado->insert(clave->Resultado->end(),(*nodo->Registros.begin()));
			}			

			int anterior = nodo->NodoAnterior;
			int siguiente = nodo->NodoSiguiente;

			if (anterior != -1)
			{
				Grupo ant = Arbol->ArchivoDatos->LeerGrupo(anterior);

				for (IteradorDeAtributos a = ant.Atributos.begin(); a!= ant.Atributos.end();a++)
				{
					if ((*a).Nombre == "NodoSiguiente")					
					{
						stringstream s;
						s << siguiente;
						(*a).Valor = s.str();
					}
				}
				Arbol->ArchivoDatos->GuardarGrupo(ant,true);
			}


			if (siguiente != -1)
			{
				Grupo sig = Arbol->ArchivoDatos->LeerGrupo(siguiente);

				for (IteradorDeAtributos a = sig.Atributos.begin(); a!= sig.Atributos.end();a++)
				{
					if ((*a).Nombre == "NodoAnterior")					
					{
						stringstream s;
						s << anterior;
						(*a).Valor = s.str();
					}

				}
				Arbol->ArchivoDatos->GuardarGrupo(sig,true);
			}


			// Elimina el grupo ya que solo contiene un registro, que es el que tiene que ser eliminado
			Arbol->ArchivoDatos->BorrarGrupo(nodo->GrupoId);

			IteradorDePunterosANodo iterador = Camino.end();
			Nodo* padre = (* (-- (--iterador)));
			Registro* regAEliminar;

			for (IteradorDeRegistros i = padre->Registros.begin(); i != padre->Registros.end(); i++)
			{
				if ( (* i).Marcado)
				{
					regAEliminar = &(* i);					
				}
			}

			ActualizarNodo(padre,regAEliminar,iterador,NULL,(padre->HijoIzquierdo == nodo->NodoId));
		}		
	}

	void EstrategiaEliminacion::BorrarRegistrosEnPolivalente(Nodo* nodo, Clave* clave,Registro* localizador){
		// Desmarca todos los registros del nodo
		for (IteradorDeRegistros i = nodo->Registros.begin(); i!= nodo->Registros.end() ; i++)
			(* i).Marcado = false;

		list<Registro>* registrosAEliminar = new list<Registro>();

		// Borra todos los registros que cumplan con la condicion
		for (IteradorDeRegistros i = nodo->Registros.begin(); i!= nodo->Registros.end() ; i++)
		{
			// Verifica que se cumplan otras condiciones
			// TODO: Verificar si no pincha por estar borrando algo con el iterador
			if (clave->OtrasCondiciones->Analizar(*i))
			{
				registrosAEliminar->insert(registrosAEliminar->end(),*i);
				(* i).Marcado = true;
			}
		}		

		int size = registrosAEliminar->size();
		// Actualiza el contador de registros afectados.
		clave->RegistrosAfectados += size;

		// Genera un array auxiliar para poder eliminar los elementos de la lista
		// Esto es necesario porque sino se debe utilizar la misma lista para iterar los elementos
		// y el iterador pierde la posicion actual en cada eliminacion
		Registro** auxiliar = new Registro*[size];
		IteradorDeRegistros iterador = registrosAEliminar->begin();

		for (int i =0; i < size ; i++)	
			auxiliar[i] = &(* iterador++);

		for (int i =0; i < size ; i++)	
		{
			if (clave->Resultado->size() == 1)
			{
				Registro primero = *clave->Resultado->begin();
				clave->RegistrosAfectados++;
				nodo->Registros.remove(*auxiliar[i]);
				nodo->Contador--;

				if ( !( primero == (*auxiliar[i]) ) )
				{
					// Agrega el registro eliminado al conjunto de resultados
					clave->Resultado->insert(clave->Resultado->end(),*auxiliar[i]);
				}
			}
			else
			{
				clave->RegistrosAfectados++;
				nodo->Registros.remove(*auxiliar[i]);
				nodo->Contador--;
				// Agrega el registro eliminado al conjunto de resultados
				clave->Resultado->insert(clave->Resultado->end(),*auxiliar[i]);
			}
		}

		// libera el array de punteros (no los datos a los que apuntaban los punteros).
		delete auxiliar;

		// Actualiza en la capa fisica el grupo de datos. Este metodo es responsable de resolver
		// el posible subflujo que se presente al guardar los registros en la capa fisica.
		if (size > 0)
			ActualizarGrupoDeDatos(nodo);	

	}

	Resultado* ConvertirResultado(int iRes)
	{
		Resultado* r = new Resultado();
		r->Codigo = static_cast<CodigoResultado>(iRes);
		return r;
	}

	Resultado* EstrategiaEliminacion::TraspasarRegistro(bool izquierdo, Nodo* padre, Grupo* nodo, Grupo* hermano, Registro* indicador, Registro traspaso){
		// Busca los registros primero y ultimo
		Registro menor = *hermano->Registros.begin();
		Registro mayor = *(--hermano->Registros.end());

		// Tiene que mover los indicadores y crear uno nuevo, ya que los nodos
		// actuales son grupos del archivo de datos y no tienen el formato de clave.
		if (izquierdo)
		{
			nodo->Registros.insert(nodo->Registros.begin(),traspaso);
			// Le pide al arbol que genere un nuevo indicador a partir del registro.
			Registro nuevoIndicador = Arbol->CrearIndicador(traspaso);

			for (IteradorDeAtributos atr = indicador->Atributos.begin();atr != indicador->Atributos.end();atr++)
				if ((* atr).Nombre == "HijoDerecho")
				{
					Atributo* a = new Atributo((* atr).Nombre,(* atr).Valor);
					a->Tipo = (*atr).Tipo;
					nuevoIndicador.Atributos.insert(nuevoIndicador.Atributos.end(),*a);
				}

				padre->Registros.remove(*indicador);
				padre->Registros.insert(padre->Registros.end(),nuevoIndicador);						
		}
		else
		{
			nodo->Registros.insert(nodo->Registros.end(),traspaso);
			//Modificar indicador
			Registro nuevoIndicador = Arbol->CrearIndicador(menor);

			for (IteradorDeAtributos atr = indicador->Atributos.begin();atr != indicador->Atributos.end();atr++)
				if ((* atr).Nombre == "HijoDerecho")
				{
					Atributo* a = new Atributo((* atr).Nombre,(* atr).Valor);
					a->Tipo = (*atr).Tipo;
					nuevoIndicador.Atributos.insert(nuevoIndicador.Atributos.end(),*a);
				}

				// Busca la posicion actual del indicador, para insertar en ese lugar el nuevo indicador
				nuevoIndicador.Marcado = true;
				for (IteradorDeRegistros i = padre->Registros.begin();i!=padre->Registros.end();i++)
					if ((*i) == *indicador)
						padre->Registros.insert(i,nuevoIndicador);

				// Luego elimina el indicador
				padre->Registros.remove(*indicador);				
		}		

		// Guarda los cambios y actualiza el nodo padre
		ActualizarCantidad(nodo);		
		CodigoResultado r = Arbol->ArchivoDatos->GuardarGrupo(*nodo);
		ActualizarCantidad(hermano);
		Arbol->ArchivoDatos->GuardarGrupo(*hermano);
		Arbol->ActualizarNodo(&padre);

		return ConvertirResultado(r);
	}

	void TraspasarNodosInternos(Nodo* padre,Nodo* destino,Nodo* origen, Registro* indicador, Registro* traspasado, bool principio, bool izquierdo){
		// Tomo el menor del hermano y lo inserto en el padre
		IteradorDeRegistros pointer = padre->Registros.end();

		for (IteradorDeRegistros reg = padre->Registros.begin();reg != padre->Registros.end(); reg++)
		{	
			if ((*reg).Marcado)
			{
				pointer = reg;
				break;
			}
		}

		padre->Registros.insert(pointer,*traspasado);
		// Tomo el indicador y lo inserto al final o al principio (dependiendo del booleano 'principio' del nodo
		if (principio)
			destino->Registros.insert(destino->Registros.begin(),*indicador);
		else
			destino->Registros.insert(destino->Registros.end(),*indicador);

		// Actualizo los punteros:
		// Coloco el hijo derecho del indicador como hijo derecho del menor
		for (IteradorDeAtributos i = indicador->Atributos.begin(); i != indicador->Atributos.end(); i++ )
		{
			if ((*i).Nombre == "HijoDerecho")
			{
				int hijoIzq;

				if (izquierdo)
					hijoIzq = destino->HijoIzquierdo;
				else
					hijoIzq = origen->HijoIzquierdo;

				for (IteradorDeAtributos  j = traspasado->Atributos.begin(); j != traspasado->Atributos.end(); j++ )
				{
					if ((*j).Nombre == "HijoDerecho")
					{
						// Coloco el hijo derecho del menor como hijo izquierdo del hermano
						if (izquierdo)
							destino->HijoIzquierdo = atoi((*j).Valor.c_str());
						else
							origen->HijoIzquierdo = atoi((*j).Valor.c_str());

						(*j).Valor = (*i).Valor;
					}
				}
				// Coloco el hijo izquierdo del hermano como hijo derecho del indicador
				stringstream ss;
				ss << hijoIzq;
				(*i).Valor = ss.str();							
			}
		}

		padre->Registros.remove(*indicador);
	}

	Resultado* EstrategiaEliminacion::FusionarNodosDatos(bool izquierdo, Nodo* padre, Grupo* nodo, Grupo* hermano, Registro* indicador){
		// Busca los registros primero y ultimo
		Registro menor = *hermano->Registros.begin();
		Registro mayor = *(--hermano->Registros.end());

		if (izquierdo)
		{
			// Mueve todos los registros desde el nodo actual hacia el hermano
			for (IteradorDeRegistros i = nodo->Registros.begin();i!= nodo->Registros.end(); i++)
				hermano->Registros.insert(hermano->Registros.end(),*i);					

			CodigoResultado resultadoFusion = Arbol->ArchivoDatos->GuardarGrupo(*hermano);

			if (resultadoFusion == Sobreflujo)
			{
				// Guarda con subflujo
				Arbol->ArchivoDatos->GuardarGrupo(*nodo,true);
				Resultado* r = new Resultado();
				r->Codigo = Exito;
				return r;
			}
			else
			{
				for (IteradorDeAtributos a = hermano->Atributos.begin(); a != hermano->Atributos.end(); a++)
				{
					if ((*a).Nombre == "NodoSiguiente")
					{
						for (IteradorDeAtributos a1 = nodo->Atributos.begin(); a1 != nodo->Atributos.end(); a1++)
							if ((*a1).Nombre == "NodoSiguiente")
							{
								if ((*a1).Valor != "-1")
								{

									Grupo g = Arbol->ArchivoDatos->LeerGrupo(atoi((*a1).Valor.c_str()));

									for (IteradorDeAtributos a2 = g.Atributos.begin(); a2 != g.Atributos.end(); a2++)
									{
										if ((*a2).Nombre == "NodoAnterior")
										{
											stringstream s;
											s << hermano->GrupoId;	
											(*a2).Valor = s.str();
										}
									}

									Arbol->ArchivoDatos->GuardarGrupo(g,true);
								}

								(*a).Valor = (*a1).Valor;
							}
					}
					else if ((*a).Nombre == "Cantidad")
					{
						stringstream s;
						s << hermano->Registros.size();
						(*a).Valor = s.str();
					}
				}

				for (IteradorDeAtributos a = indicador->Atributos.begin(); a != indicador->Atributos.end(); a++)
					if ((*a).Nombre == "HijoDerecho")				
					{	
						stringstream s;
						s << nodo->GrupoId;				
						(*a).Valor = s.str();
					}

					// Libera el nodo actual y guarda los cambios en el hermano
					Arbol->ArchivoDatos->BorrarGrupo(nodo->GrupoId);
					Arbol->ArchivoDatos->GuardarGrupo(*hermano,true);	

					// Borrar el indicador en el nodo padre haciendo una llamada recursiva
					IteradorDePunterosANodo it = --Camino.end();
					return ActualizarNodo(padre,indicador,--it,hermano,false);
			}
		}
		else
		{
			// Mueve todos los registros desde el hermano actual hacia el nodo
			for (IteradorDeRegistros i = hermano->Registros.begin(); i!= hermano->Registros.end(); i++)
				nodo->Registros.insert(nodo->Registros.end(),*i);

			CodigoResultado resultadoFusion = Arbol->ArchivoDatos->GuardarGrupo(*nodo);

			if (resultadoFusion == Sobreflujo)
			{
				for (IteradorDeRegistros i = hermano->Registros.begin(); i!= hermano->Registros.end(); i++)
					nodo->Registros.remove(*i);

				Arbol->ArchivoDatos->GuardarGrupo(*nodo,true);

				Resultado* r = new Resultado();
				r->Codigo = Exito;
				return r;
			}
			else
			{


				for (IteradorDeAtributos a = nodo->Atributos.begin(); a != nodo->Atributos.end(); a++)
				{
					if ((*a).Nombre == "NodoSiguiente")
					{
						for (IteradorDeAtributos a1 = hermano->Atributos.begin(); a1 != hermano->Atributos.end(); a1++)
							if ((*a1).Nombre == "NodoSiguiente")
							{
								if ((*a1).Valor != "-1")
								{
									Grupo g = Arbol->ArchivoDatos->LeerGrupo(atoi((*a1).Valor.c_str()));

									for (IteradorDeAtributos a2 = g.Atributos.begin(); a2 != g.Atributos.end(); a2++)
									{
										if ((*a2).Nombre == "NodoAnterior")
										{
											stringstream s;
											s << nodo->GrupoId;	
											(*a2).Valor = s.str();
										}
									}

									Arbol->ArchivoDatos->GuardarGrupo(g,true);
 								}

								(*a).Valor = (*a1).Valor;
							}
					}
					else if ((*a).Nombre == "Cantidad")
					{
						stringstream s;
						s << nodo->Registros.size();
						(*a).Valor = s.str();
					}
				}

				for (IteradorDeAtributos a = indicador->Atributos.begin(); a != indicador->Atributos.end(); a++)
					if ((*a).Nombre == "HijoDerecho")				
					{	
						stringstream s;
						s << nodo->GrupoId;				
						(*a).Valor = s.str();
					}

					// Libera el nodo hermano y guarda los cambios en el nodo actual
					Arbol->ArchivoDatos->BorrarGrupo(hermano->GrupoId);
					Arbol->ArchivoDatos->GuardarGrupo(*nodo,true);


					// Borrar el indicador en el nodo padre haciendo una llamada recursiva
					IteradorDePunterosANodo it = --Camino.end();
					return ActualizarNodo(padre,indicador,--it,nodo,false);
			}
		}
	}

	Resultado* EstrategiaEliminacion::FusionarNodos(bool izquierdo, Nodo* padre, Nodo* nodo, Nodo* hermano, Registro* indicador, IteradorDePunterosANodo caminoPtr){
		// Busca los registros primero y ultimo
		Registro menor = *hermano->Registros.begin();
		Registro mayor = *(--hermano->Registros.end());

		if (izquierdo)
		{
			// Copio el indicador
			Registro* r = new Registro(*indicador);

			//Modifico el hijo derecho del nuevo indicador
			for (IteradorDeAtributos a = r->Atributos.begin(); a != r->Atributos.end(); a++)
			{
				if ((*a).Nombre == "HijoDerecho")
				{
					stringstream s;
					s << nodo->HijoIzquierdo;
					(*a).Valor = s.str();
				}
			}

			// Bajo el indicador
			hermano->Registros.push_back(*r);			

			//hermano->Registros.insert(hermano->Registros.end(),mayor);
			// Mueve todos los registros desde el nodo actual hacia el hermano
			for (IteradorDeRegistros i = nodo->Registros.begin();i!= nodo->Registros.end(); i++)
				hermano->Registros.insert(hermano->Registros.end(),*i);		

			// Libera el nodo hermano y guarda los cambios en el nodo actual			
			hermano->Contador = hermano->Registros.size();
			Resultado resAc = Arbol->ActualizarNodo(&hermano);

			if (resAc.Codigo == Exito)
			{
				for (IteradorDeAtributos a = indicador->Atributos.begin(); a != indicador->Atributos.end(); a++)
					if ((*a).Nombre == "HijoDerecho")				
					{	
						stringstream s;
						s << nodo->GrupoId;				
						(*a).Valor = s.str();
					}

					// Libera el nodo actual y guarda los cambios en el hermano			
					Arbol->EliminarNodo(nodo->NodoId);			
			}
			else
			{
				// Remueve todos los registros desde el hermano actual hacia el nodo
				for (IteradorDeRegistros i = nodo->Registros.begin();i!= nodo->Registros.end(); i++)
					hermano->Registros.remove(*i);

				hermano->Registros.remove(*r);

				delete r;

				Resultado* res2 = new Resultado();
				res2->Codigo = Error;
				return res2;
			}

		}
		else
		{
			// Copio el indicador
			Registro* r = new Registro(*indicador);

			//Modifico el hijo derecho del nuevo indicador
			for (IteradorDeAtributos a = r->Atributos.begin(); a != r->Atributos.end(); a++)
			{
				if ((*a).Nombre == "HijoDerecho")
				{
					stringstream s;
					s << hermano->HijoIzquierdo;
					(*a).Valor = s.str();
				}
			}

			// Bajo el indicador
			nodo->Registros.push_back(*r);			

			// Mueve todos los registros desde el hermano actual hacia el nodo
			for (IteradorDeRegistros i = hermano->Registros.begin();
				i!= hermano->Registros.end(); i++)
				nodo->Registros.push_back(*i);

			nodo->Contador = nodo->Registros.size();
			// Libera el nodo hermano y guarda los cambios en el nodo actual			
			Resultado resAc = Arbol->ActualizarNodo(&nodo);

			if (resAc.Codigo == Exito)
			{
				for (IteradorDeAtributos a = indicador->Atributos.begin(); a != indicador->Atributos.end(); a++)
					if ((*a).Nombre == "HijoDerecho")				
					{	
						stringstream s;
						s << nodo->GrupoId;				
						(*a).Valor = s.str();
					}

					Arbol->EliminarNodo(hermano->NodoId);				
			}
			else
			{
				// Remueve todos los registros desde el hermano actual hacia el nodo
				for (IteradorDeRegistros i = hermano->Registros.begin();
					i!= hermano->Registros.end(); i++)
					nodo->Registros.remove(*i);

				nodo->Registros.remove(*r);

				delete r;

				Resultado* res2 = new Resultado();
				res2->Codigo = Error;
				return res2;

			}
		}

		// Borrar el indicador en el nodo padre haciendo una llamada recursiva
		return ActualizarNodo(padre,indicador,caminoPtr,NULL,false);
	}

	int ObtenerHermano(Nodo* padre, Registro** indicador, bool* izquierdo){

		bool encontrado = false;
		// Indica si tuvo que resolver el subflujo con un hermano izquerdo,
		// ya que no posee uno derecho
		*izquierdo = false;

		Registro* indicadorAnterior;
		int hermanoId;

		// Busca el registro marcado, que indica que es el registro siguiente al puntero que contiene
		// el registro que se quiere eliminar
		for (IteradorDeRegistros i = padre->Registros.begin(); i != padre->Registros.end(); i++)
		{
			if ((* i).Marcado){ 
				encontrado = true;
				*indicador = &(* i);
				// Busca el hijo derecho del hermano, que contiene los datos
				for (IteradorDeAtributos j = (*indicador)->Atributos.begin(); j!= (*indicador)->Atributos.end();j++)
					if ((*j).Nombre == "HijoDerecho")
						return hermanoId = atoi((*j).Valor.c_str());				
			}
		}

		// Si no pudo encontrar un registro marcado entonces significa que el nodo es el ultimo
		// hijo del padre, por lo que solo tiene hermano izquierdo.
		if (!encontrado)
		{
			*izquierdo = true;
			IteradorDeRegistros it = --padre->Registros.end();
			*indicador = &(* it);

			if (padre->Registros.size() > 1)
			{
				// Recorre en forma inversa la lista para hallar el hno izquierdo

				indicadorAnterior = &(* --it);				

				// Busca el hijo derecho del hermano, que contiene los datos
				for (IteradorDeAtributos j = indicadorAnterior->Atributos.begin(); j!= indicadorAnterior->Atributos.end();j++)
					if ((*j).Nombre == "HijoDerecho")
						return hermanoId = atoi((*j).Valor.c_str());
			}else
				// El hermano izquierdo es el primer hijo del padre
				return padre->HijoIzquierdo;
		}



		return -1;
	}

	Resultado* EstrategiaEliminacion::ActualizarGrupoDeDatos(Nodo* nodo){

		IteradorDePunterosANodo caminoPtr = --this->Camino.end();

		ActualizarCantidad(nodo);

		int iRes = Arbol->ArchivoDatos->GuardarGrupo(*nodo);
		Resultado* r = ConvertirResultado(iRes);

		if (r->Codigo == Exito)	
		{			
			return r;
		}
		else if (r->Codigo == Error)	
		{
			r->mensaje = "Se produjo un error al intentar actualizar un nodo: " + r->mensaje;
			return r;
		}
		else if (r->Codigo == Subflujo)
		{
			// Resolver subflujo:
			// Pedirle al hermano izquierdo o derecho si puede cederle una clave
			Nodo* padre = (* --caminoPtr);

			Registro** indicador = new Registro*;
			bool* izquierdo = new bool;
			*izquierdo = false;
			int hermanoId = ObtenerHermano(padre,indicador,izquierdo);

			// Intenta pedirle al hermano derecho si existe
			Grupo* nodoHermano;
			// Dependiendo si el registro es un registro de datos, busca el grupo en el archivo
			// maestro o en el archivo de indices (Nodo)
			if (hermanoId != -1)
				nodoHermano = &Arbol->ArchivoDatos->LeerGrupo(hermanoId);			
			else
			{
				cerr << "CapaIndices->EstrategiaBorrado.cpp->EstrategiaEliminacion::ActualizarGrupoDeDatos(Nodo* nodo)" << endl;
				throw new exception();
			}

			if (nodoHermano->Registros.size() == 0)
			{
				int iRes2 = Arbol->ArchivoDatos->GuardarGrupo(*nodo,true);
				Resultado* r = ConvertirResultado(iRes2);
				return r;
			}


			// Busca los registros primero y ultimo
			Registro menor = *nodoHermano->Registros.begin();
			Registro mayor = *(--nodoHermano->Registros.end());

			// Si el hermano es izquierdo, le pide el registro mayor. Sino, el menor
			if (*izquierdo)
			{
				nodoHermano->Registros.remove(mayor);
				ActualizarCantidad(nodoHermano);
			}
			else
			{
				nodoHermano->Registros.remove(menor);
				ActualizarCantidad(nodoHermano);
			}

			// Actualiza el grupo, en su correspondiente archivo			
			r = ConvertirResultado(Arbol->ArchivoDatos->GuardarGrupo(*nodoHermano));			

			// Si el hermano queda tambien en subflujo, los fusiona
			if (r->Codigo== Subflujo)
			{
				// Restaura la lista de registros
				if (*izquierdo)
				{
					nodoHermano->Registros.insert(nodoHermano->Registros.end(),mayor);
					ActualizarCantidad(nodoHermano);
				}
				else
				{
					nodoHermano->Registros.insert(nodoHermano->Registros.begin(),menor);
					ActualizarCantidad(nodoHermano);
				}

				// Fusiona los nodos y actualiza los nodos padres involucrados
				return FusionarNodosDatos(*izquierdo,padre,nodo,nodoHermano,*indicador);
			}
			else
			{
				Resultado* rtras;

				if (*izquierdo)
					// Traspasa el registro desde el hermano hacia el nodo modificado y guarda todos los cambios
					rtras = TraspasarRegistro(*izquierdo,padre,nodo,nodoHermano,*indicador,mayor);
				else
					// Traspasa el registro desde el hermano hacia el nodo modificado y guarda todos los cambios
					rtras = TraspasarRegistro(*izquierdo,padre,nodo,nodoHermano,*indicador,menor);
				if (rtras->Codigo == Subflujo)
					return ActualizarGrupoDeDatos(nodo);
			}
		}	
		else
		{
			stringstream stream;
			stream << "Codigo de resultado desconocido: " << r->Codigo << ". Mensaje: " << r->mensaje;
			r->mensaje = stream.str();
			return r;
		}
	}


	Resultado* EstrategiaEliminacion::ActualizarNodo(Nodo *nodo, Registro *regAEliminar, IteradorDePunterosANodo caminoPtr, Grupo* fusionado, bool hijoIzquierdo){
		
		if (hijoIzquierdo)
		{
			for(IteradorDeAtributos a = regAEliminar->Atributos.begin(); a != regAEliminar->Atributos.end(); a++)
			{
				if (a->Nombre == "HijoDerecho")
				{
					nodo->HijoIzquierdo = atoi(a->Valor.c_str());
				}
			}
		}
		
		nodo->Registros.remove(*regAEliminar);
		nodo->Contador = nodo->Registros.size();
		ActualizarCantidad(nodo);
		Resultado* r = &Arbol->ActualizarNodo(&nodo);

		if (r->Codigo == Exito)	
		{			
			r = new Resultado();
			r->Codigo = Exito;
			return r;
		}
		else if (r->Codigo == Error)	
		{
			r->mensaje = "Se produjo un error al intentar actualizar un nodo: " + r->mensaje;
			return r;
		}
		else if (r->Codigo == Subflujo)
		{
			// Si el subflujo se produjo en la raiz, lo grabo igual y termino la llamada recursiva
			if (nodo->NodoId == Arbol->ObtenerNodoRaiz()->NodoId)
			{
				if (nodo->Registros.size()==0)
				{
					if (nodo->Tipo == Interno)
					{
						Nodo** nuevaRaiz = new Nodo*;
						if (fusionado != NULL)
							*nuevaRaiz = new Nodo(*fusionado);
						else
							Arbol->BuscarNodo(nodo->HijoIzquierdo,nuevaRaiz);

						nodo->Atributos.clear();
						nodo->HijoIzquierdo = (*nuevaRaiz)->HijoIzquierdo;
						nodo->Contador = (*nuevaRaiz)->Contador;
						nodo->Tipo = (*nuevaRaiz)->Tipo;

						for (IteradorDeRegistros i = (*nuevaRaiz)->Registros.begin(); i != (*nuevaRaiz)->Registros.end();i++)
							nodo->Registros.insert(nodo->Registros.end(),*i);

						Arbol->EliminarNodo((*nuevaRaiz)->NodoId);
						nodo->Sincronizar(false);
						Arbol->ActualizarNodo(&nodo);				
					}
					else
					{
						Grupo g = Arbol->ArchivoDatos->LeerGrupo(nodo->HijoIzquierdo);
						
						if (g.Registros.size() == 0 )
						{
							Arbol->ArchivoDatos->BorrarGrupo(nodo->HijoIzquierdo);
							nodo->HijoIzquierdo = -1;
							nodo->Contador = 0;
							Arbol->ActualizarNodo(&nodo,true);

							Resultado* r = new Resultado();
							r->Codigo = Exito;
							return r;
						}

						Registro* r = &(* g.Registros.begin());

						Registro* nuevoIndicador = new Registro();
						list<Atributo>* nuevoIndicadorCampos = ExtraerAtributosClave(&(r->Atributos),Arbol->Metadata->Claves);

						Atributo* a = new Atributo();
						a->Nombre ="HijoDerecho";
						stringstream s;
						s<< g.GrupoId;
						a->Valor = s.str();
						a->Tipo = EnteroLargo;
						nuevoIndicadorCampos->push_back(*a);

						nuevoIndicador->Atributos = *nuevoIndicadorCampos;
						nodo->Registros.push_back(*nuevoIndicador);

						Grupo g2;
						Arbol->ArchivoDatos->CrearGrupo(g2);

						Atributo ant2("NodoAnterior","-1");
						ant2.Tipo = EnteroLargo;

						Atributo sig2("NodoSiguiente",s.str());
						sig2.Tipo = EnteroLargo;

						Atributo cont2("Cantidad","0");
						cont2.Tipo = Entero;

						g2.Atributos.push_back(cont2);
						g2.Atributos.push_back(ant2);
						g2.Atributos.push_back(sig2);

						nodo->HijoIzquierdo = g2.GrupoId;
						Arbol->ArchivoDatos->GuardarGrupo(g2,true);

						for (IteradorDeAtributos a = g.Atributos.begin(); a != g.Atributos.end(); a++)
						{
							if ((*a).Nombre == "NodoAnterior")
							{
								stringstream ss;
								ss << g2.GrupoId;
								(*a).Valor = ss.str();
							}
						}

						nodo->Tipo = Hoja;
						nodo->Contador = 1;	
						nodo->Sincronizar(false);
						Arbol->ArchivoDatos->GuardarGrupo(g,true);
						Arbol->ActualizarNodo(&nodo,true);
					}

				}

				// Graba con subflujo (es la raíz)
				&Arbol->ActualizarNodo(&nodo,true);

				// TODO: Guardar igual el nodo raiz				
				r = new Resultado();
				r->Codigo = Exito;
				return r;
			}

			// Resolver subflujo:
			// Pedirle al hermano izquierdo o derecho si puede cederle una clave
			Nodo* padre = *(--caminoPtr);

			Registro** indicador = new Registro*;
			bool* izquierdo = new bool;
			int hermanoId = ObtenerHermano(padre,indicador,izquierdo);

			// Intenta pedirle al hermano derecho si existe
			Nodo** nodoHermano = new Nodo*;
			// Dependiendo si el registro es un registro de datos, busca el grupo en el archivo
			// maestro o en el archivo de indices (Nodo)
			*r = Arbol->BuscarNodo(hermanoId,nodoHermano);

			// Busca los registros primero y ultimo
			Registro menor = *(* nodoHermano)->Registros.begin();
			Registro mayor = *(--(* nodoHermano)->Registros.end());

			// Si el hermano es izquierdo, le pide el registro mayor. Sino, el menor
			if (*izquierdo)
			{
				(* nodoHermano)->Registros.remove(mayor);
				(* nodoHermano)->Contador--;
			}
			else
			{
				(* nodoHermano)->Registros.remove(menor);
				(* nodoHermano)->Contador--;
			}



			// Actualiza el grupo, en su correspondiente archivo			
			*r = Arbol->ActualizarNodo(nodoHermano);

			// Si el hermano queda tambien en subflujo, los fusiona
			if (r->Codigo == Subflujo)
			{
				// Restaura la lista de registros
				if (*izquierdo)
				{
					(* nodoHermano)->Registros.insert((* nodoHermano)->Registros.end(),mayor);
					(* nodoHermano)->Contador++;
				}
				else
				{
					(* nodoHermano)->Registros.insert((* nodoHermano)->Registros.begin(),menor);
					(* nodoHermano)->Contador++;
				}

				// Fusiona los nodos y actualiza los nodos padres involucrados
				Resultado* fusion = FusionarNodos(*izquierdo,padre,nodo,*nodoHermano,*indicador,caminoPtr);

				// Si no logra fusionar los datos, fuerza la grabación del nodo hermano en subflujo
				if (fusion->Codigo != Exito)
				{
					// En este caso, solo tiene que desplazar los indicadores
					// ya que todos los grupos involucrados son nodos
					TraspasarNodosInternos(padre,nodo,*nodoHermano,*indicador,&mayor,true,*izquierdo);					

					if ((*nodoHermano)->Registros.size() == 0)
						Arbol->EliminarNodo((*nodoHermano)->NodoId);
					else
						Arbol->ActualizarNodo(nodoHermano,true);

					// Guarda los cambios
					Arbol->ActualizarNodo(&nodo,true);
					Arbol->ActualizarNodo(&padre,true);

					Resultado* r2 = new Resultado();
					r2->Codigo = Exito;
					return r2;
				}
				else
					return fusion;
			}
			else
			{
				// En este caso, solo tiene que desplazar los indicadores
				// ya que todos los grupos involucrados son nodos
				if (*izquierdo)
					TraspasarNodosInternos(padre,nodo,*nodoHermano,*indicador,&mayor,true,true);				
				else
					TraspasarNodosInternos(padre,nodo,*nodoHermano,*indicador,&menor,false,false);

				// Guarda los cambios
				Arbol->ActualizarNodo(nodoHermano,true);
				Arbol->ActualizarNodo(&nodo,true);
				Arbol->ActualizarNodo(&padre,true);

				Resultado* r2 = new Resultado();
				r2->Codigo = Exito;
				return r2;
			}
		}
		else
		{
			stringstream stream;
			stream << "Codigo de resultado desconocido: " << r->Codigo << ". Mensaje: " << r->mensaje;
			r = new Resultado();
			r->mensaje = stream.str();
			return r;
		}
	}
}

#endif

